package com.example.config;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import java.lang.reflect.Method;

/**
 * 模拟Web配置文件。其实主要是配合@Configuration，测试注解功效。<p>
 * {@code @Import}：配置类与配置类之间的导入，提供类似于 Spring XML 中的{@code <import/>}元素的功能。简单理解：将指定的配置类导入当前配置，合成一个配置类。<p>
 *
 * @author yexi
 */
@Configuration
@Import(DatabaseConfiguration.class)
public class WebConfiguration {

    @Value("${web.port}")
    private Integer port;

    @Setter
    @Getter
    @ToString
    public static class MyServer {
        private Integer port;
    }

    @Bean
    public MyServer myServer() {
        MyServer myServer = new MyServer();
        myServer.setPort(port);
        return myServer;
    }

    /**
     * 测试"bean之间引用"。
     * 效果：1、因为bean只会有一个，所以@Bean的实例方法实际上只会调用一次，后续通过spring的动态代理后并不会再实际执行实例方法里面的内容了。
     * 而直接从容器中获取到对应的bean返回。
     * 2、不采用内部方法调用而直接采用形参方式置于实例方法也会是一样的情况。
     * TIPS:本质上，只要是@Bean就会被动态代理，而不是因为bean之间引用而触发。因此如果说存在两相同的bean实例方法，则另外一个里面的内容将不会调用。
     *
     * @see org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#isCurrentlyInvokedFactoryMethod(Method method)
     * @see org.springframework.context.annotation.ConfigurationClassEnhancer.BeanMethodInterceptor#resolveBeanReference(Method beanMethod, Object[] beanMethodArgs, ConfigurableBeanFactory beanFactory, String beanName)
     */
    @Bean("componentClass")
    public ComponentClass componentClass1() {
        System.out.println("该componentClass的bean实例方法被成功调用，且由于@Bean的解析优先度缘故，@Component注解的注入被取消，改而通过此方法注入");
        //由于代理的缘故，其实只会成功调用一次实例方法
        commonBean();
        commonBean();
        commonBean();
        commonBean();
        commonBean();
        commonBean();
        commonBean();
        ComponentClass componentClass = new ComponentClass(commonBean());
        return componentClass;
    }

    //bean名称不相同就不会被识别为重复
//    @Bean("componentClass2")
    @Bean("componentClass")
    public ComponentClass componentClass2(CommonBean commonBean) {
        System.out.println("由于该bean与上方的bean重复了，所以这个实例方法本质上是不会调用的");
        ComponentClass componentClass = new ComponentClass(commonBean);
        return componentClass;
    }


    @Bean
    public CommonBean commonBean() {
        System.out.println("commonBean的@Bean方法被调用，创建bean对象");
        return new CommonBean();
    }

}
